//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <ddk.h>

#include "ctrl.h"
#include "consoledev.h"

#ifdef _DEBUG
#define Assert(x)   if (!((bool)(x))) { \
                        DebugBreak(); \
                    }
#else
#define Assert(x)
#endif

#define MAXLINELENGTH  80

EXTERN_C void PrintComPort(const char *szMessage);

IDeviceDriver     *g_pConsoleDev = NULL;

//
// The ConsoleDev driver's classfactory
//
EXTERN IDeviceDriver * CDECL CreateConsoleDev()
{
    ECode ec;

    g_pConsoleDev = new ConsoleDev;
    if (NULL == g_pConsoleDev) {
        Assert(0 && "There have no enough memory");
        return NULL;
    }

    // Initialize keyboard hardware
    ec = ((ConsoleDev*)g_pConsoleDev)->Initialize();
    if (FAILED(ec)) {
        delete g_pConsoleDev;
        g_pConsoleDev = NULL;
        return NULL;
    }

    g_pConsoleDev->AddRef();

    return g_pConsoleDev;
}

ECode ConsoleDev::Initialize()
{
    m_pWriteBuf = (char *)malloc((MAXLINELENGTH + 1) * sizeof(char));
    if (NULL == m_pWriteBuf) {
        return E_OUT_OF_MEMORY;
    }

    m_bConsoleEnabled = TRUE;

    return NOERROR;
}

EXTERN_C bool_t CDECL BspReadComPort(ComPort comPort, uint8_t *pu8Data);

ECode ConsoleDev::Read(
        /* [in] */ UInt64 u64Offset,
        /* [in] */ UInt32 uNumberOfBytesToRead,
        /* [out] */ EzByteBuf ebbData,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    if (!ebbData.GetCapacity()
        || (int)uNumberOfBytesToRead > ebbData.GetCapacity()
        || 0 == uNumberOfBytesToRead) {
        return E_INVALID_ARGUMENT;
    }

    if (!m_bConsoleEnabled) return NOERROR;

    uint8_t *p = (uint8_t *)(char *)ebbData;
    BspReadComPort(COMPORT_PRINT, p);
    ebbData.SetUsed(1);

    return NOERROR;
}

ECode ConsoleDev::WriteToCOM(EzByteBuf ebbData)
{
    uint32_t len;
    char *p = (char *)ebbData;
    uint32_t c = ebbData.GetUsed();

    while (c > 0) {
        len = (c > MAXLINELENGTH) ? MAXLINELENGTH : c;
        for (uint32_t i = 0; i < len; i++) {
            m_pWriteBuf[i] = *p;
            p++;
        }
        m_pWriteBuf[len] = '\0';
        PrintComPort(m_pWriteBuf);
        c -= len;
    }

    return NOERROR;
}

ECode ConsoleDev::Write(
        /* [in] */ UInt64 u64Offset,
        /* [in] */ EzByteBuf ebbData,
        /* [out] */ UInt32 * puNumberOfBytesWritten,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    if (!m_bConsoleEnabled) return NOERROR;

    return WriteToCOM(ebbData);
}

ECode ConsoleDev::Control(
        /* [in] */ Int32 nControlCode,
        /* [in] */ EzByteBuf ebbInData,
        /* [out] */ EzByteBuf * pOutData,
        /* [out] */ IEvent * * ppCompletionEvent)
{
    ECode ec;

    if ((nControlCode < 0) || (nControlCode > 12)
        || (ppCompletionEvent && *ppCompletionEvent)) {
        return E_INVALID_ARGUMENT;
    }

    switch (nControlCode) {
        case CTRL_SETCURSORPOS: { // Set the Cursor's position
            ec = NOERROR;

            break;
        }

        case CTRL_GETCURSORPOS:  { // Get the Cursor's position
            if ((uint32_t)pOutData->GetCapacity() < sizeof(uint32_t)
                || !ebbInData.IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            *(uint32_t *)pOutData->GetPayload() = 0;//m_uCursorPosition;

            pOutData->SetUsed(sizeof(uint32_t));
            ec = NOERROR;

            break;
        }


        case CTRL_CLEARSCREEN: {  // Clear the screen
            ec = NOERROR;

            break;
        }


        case CTRL_SETCURSORSHAPE: {  // Set the cursor shape
            ec = NOERROR;

            break;
        }


        case CTRL_GETSCREENCOL: {  // Get the screen columns;
            if ((uint32_t)pOutData->GetCapacity() < sizeof(uint32_t)
                || !ebbInData.IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            *(uint32_t *)pOutData->GetPayload() = 320; // m_uScreenColumn;
            ec = NOERROR;

            break;
        }

        case CTRL_GETSCREENROW: {  // Get the screen rows;
            if ((uint32_t)pOutData->GetCapacity() < sizeof(uint32_t)
                || !ebbInData.IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            *(uint32_t *)pOutData->GetPayload() = 240; // m_uScreenRow;
            ec = NOERROR;

            break;
        }

        case CTRL_CONFIGKEYBOARD: {
            ec = S_FALSE;

            break;
        }

        case CTRL_MOVECURSOR:  { //move the cursor forward
            if (!pOutData->IsNull()) {
                return E_INVALID_ARGUMENT;
            }

            uint32_t uMove = *(uint32_t*)ebbInData.GetPayload();
            if (uMove > MAXLINELENGTH) {
                uMove = MAXLINELENGTH;
            }

            for (uint32_t i = 0; i < uMove; i++) {
                m_pWriteBuf[i] = '\b';
            }
            m_pWriteBuf[uMove] = '\0';
            PrintComPort(m_pWriteBuf);
            ec = NOERROR;

            break;
        }

        case CTRL_DISABLECONSOLE: { //disable console and enable serial2 irq
            m_bConsoleEnabled = FALSE;
            DzEnableIrq(21);
            ec = NOERROR;

            break;
        }

        case CTRL_ENABLECONSOLE: { //enable console and disable serial2 irq
            m_bConsoleEnabled = TRUE;
            DzDisableIrq(21);
            ec = NOERROR;

            break;
        }

        default:
            ec = E_NOT_IMPLEMENTED;
    }

    return ec;
}
